package net.lindeer.lang

interface Stmt {
    interface Visitor<R> {
        fun visitExpressionStmt(stmt: Expression): R

        fun visitPrintStmt(stmt: Print): R

        fun visitVarStmt(stmt: Var): R

        fun visitBlockStmt(stmt: Block): R

        fun visitIfStmt(stmt: If): R

        fun visitWhileStmt(stmt: While): R

        fun visitFuncStmt(stmt: Func): R

        fun visitReturnStmt(stmt: Return): R

        fun visitClazzStmt(stmt: Clazz): R
    }

    fun <R> accept(visitor: Visitor<R>): R

    class Expression(val expression: Expr) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitExpressionStmt(this)
        }
    }

    class Print(val expression: Expr) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitPrintStmt(this)
        }
    }

    class Var(val name: Token, val initializer: Expr?) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitVarStmt(this)
        }
    }

    class Block(val statements: List<Stmt>) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitBlockStmt(this)
        }
    }

    class If(val condition: Expr, val thenStmt: Stmt, val elseStmt: Stmt?) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitIfStmt(this)
        }
    }

    class While(val condition: Expr, val body: Stmt) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitWhileStmt(this)
        }
    }

    class Func(val name: Token, val params: List<Token>, val body: List<Stmt>) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitFuncStmt(this)
        }
    }

    class Return(val keyword: Token, val value: Expr?) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitReturnStmt(this)
        }
    }

    class Clazz(val name: Token,
                val superclass: Expr.Variable?,
                val methods: List<Func>) : Stmt {
        override fun <R> accept(visitor: Visitor<R>): R {
            return visitor.visitClazzStmt(this)
        }
    }
}
